package com.xiaowu.xblog.service;


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xiaowu.xblog.constant.MessageConstant;
import com.xiaowu.xblog.dto.UserCreateDTO;
import com.xiaowu.xblog.dto.UserLoginDTO;
import com.xiaowu.xblog.entity.LoginUser;
import com.xiaowu.xblog.entity.SysUser;
import com.xiaowu.xblog.exception.OAuth2Exception;
import com.xiaowu.xblog.exception.UserNotLoginException;
import com.xiaowu.xblog.model.UserLoginVO;
import com.xiaowu.xblog.model.UserVO;
import com.xiaowu.xblog.properties.GithubAppProperties;
import com.xiaowu.xblog.repository.SysUserRepository;
import com.xiaowu.xblog.service.impl.BaseServiceImpl;
import com.xiaowu.xblog.utils.JwtUtil;
import com.xiaowu.xblog.utils.Result;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.*;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.beans.PropertyDescriptor;
import java.util.*;

@Service
//@AllArgsConstructor
@Slf4j
public class SysUserService extends BaseServiceImpl<SysUserRepository, SysUser> {
    // 可添加用户业务方法

    private final RedisTemplate<String, String> redisTemplate;
    private final AuthenticationManager authenticationManager;
    private final GithubAppProperties githubAppProperties;
    private final RestTemplate restTemplate;
    private final ObjectMapper objectMapper;

    protected SysUserService(SysUserRepository repository, RedisTemplate<String, String> redisTemplate, AuthenticationManager authenticationManager, GithubAppProperties githubAppProperties, RestTemplate restTemplate, ObjectMapper objectMapper) {
        super(repository);
        this.redisTemplate = redisTemplate;
        this.authenticationManager = authenticationManager;
        this.githubAppProperties = githubAppProperties;
        this.restTemplate = restTemplate;
        this.objectMapper = objectMapper;
    }

    @Transactional(rollbackFor = Exception.class)
    public UserLoginVO login(UserLoginDTO userLoginDTO) {

//         Step 3: 用户认证
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        userLoginDTO.getLoginInput(),
                        userLoginDTO.getPassword()
                )
        );

        if (authentication == null) {
            throw new UserNotLoginException("登录失败");
        }

        // Step 4: 获取用户信息并生成 Token
        SecurityContextHolder.getContext().setAuthentication(authentication);
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        SysUser user = loginUser.getUser();

        String token = JwtUtil.generateToken(user.getId(), user.getUsername(), MessageConstant.ISUSER);

        return UserLoginVO.builder()
                .id(user.getId())
                .username(user.getUsername())
                .avatarUrl(user.getAvatarUrl())
                .token(token)
                .build();
    }

    @Transactional(rollbackFor = Exception.class)
    public int insertUser(UserCreateDTO userCreateDTO) {
        if(userCreateDTO == null){
            throw new UserNotLoginException(MessageConstant.LOGIN_FAILED);
        }


        String password = userCreateDTO.getPassword();
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        String password1 = encoder.encode(password);
        SysUser user = new SysUser();
        //属性拷贝
        BeanUtils.copyProperties(userCreateDTO,user);
        user.setPassword(password1);
        boolean b = this.create(user);
        if(b){
            return 1;
        }
        return 0;
    }

    @Transactional(rollbackFor = Exception.class)
    public UserVO getUserById(Long id) {
        SysUser byId = this.findById(id);
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(byId,userVO);
        return userVO;
    }


    @Transactional(rollbackFor = Exception.class)
    public int updateUser(UserCreateDTO userDTO) {
        // 参数验证
        validateUpdateUserParams(userDTO);

        // 查找现有用户
        SysUser user = this.findById(userDTO.getId());
        if (user == null) {
            throw new RuntimeException("用户不存在，ID: " + userDTO.getId());
        }

        // 使用Builder模式或反射进行字段更新
        updateUserFieldsV2(user, userDTO);

        // 执行更新return
        boolean b = this.updateById(user);
        return b ? 1 : 0;
    }

    /**
     * 验证更新用户参数
     */
    private void validateUpdateUserParams(UserCreateDTO userDTO) {
        if (userDTO == null) {
            throw new IllegalArgumentException("用户信息不能为空");
        }
        if (userDTO.getId() == null) {
            throw new IllegalArgumentException("用户ID不能为空");
        }
    }

// 推荐的进一步优化版本 - 使用反射或工具类
    /**
     * 使用BeanUtils进行属性复制的版本
     */
    private void updateUserFieldsV2(SysUser user, UserCreateDTO userDTO) {
        // 创建临时对象用于复制
        SysUser tempUser = new SysUser();
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        // 只复制非空字段
        BeanUtils.copyProperties(userDTO, tempUser, getNullPropertyNames(userDTO));

        // 特殊处理密码字段
        if (StringUtils.hasText(userDTO.getPassword())) {
            tempUser.setPassword(passwordEncoder.encode(userDTO.getPassword()));
        }

        // 将非空字段复制到目标对象
        BeanUtils.copyProperties(tempUser, user, getNullPropertyNames(tempUser));
    }

    /**
     * 获取对象中为null的属性名
     */
    private String[] getNullPropertyNames(Object source) {
        final BeanWrapper src = new BeanWrapperImpl(source);
        PropertyDescriptor[] pds = src.getPropertyDescriptors();

        Set<String> emptyNames = new HashSet<>();
        for (PropertyDescriptor pd : pds) {
            Object srcValue = src.getPropertyValue(pd.getName());
            if (srcValue == null || (srcValue instanceof String && !StringUtils.hasText((String) srcValue))) {
                emptyNames.add(pd.getName());
            }
        }
        return emptyNames.toArray(new String[0]);
    }




    public Result<Object> loginByGithubCode(String code) {

        try {
            String accessToken = getAccessToken(code);

            //todo 后续debug
            Map<String, Object> userInfo = getUserInfo(accessToken);

            BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

            Object idObj = userInfo.get("id");
            Object loginObj = userInfo.get("login");
            Object avatarUrlObj = userInfo.get("avatar_url");

            if (idObj == null || loginObj == null || avatarUrlObj == null) {
                log.error("缺少必要字段，GitHub返回：{}", userInfo);
                throw new OAuth2Exception("GitHub 用户信息不完整");
            }

            String githubId = idObj.toString();

            SysUser user = repository.findByGithubId(githubId);

            if (user == null) {
                user = new SysUser();
                user.setGithubId(githubId);
                user.setUsername(userInfo.get("login").toString());
                user.setAvatarUrl(userInfo.get("avatar_url").toString());
                // 修复：为GitHub用户生成随机密码或使用默认密码
                String defaultPassword = "123456"; // 生成随机密码
                // 或者使用: String defaultPassword = "github_oauth_user"; // 使用默认密码
                user.setPassword(passwordEncoder.encode(defaultPassword));
                this.create(user);
            }

            String jwtToken = JwtUtil.generateToken(user.getId(), user.getUsername(), MessageConstant.ISUSER);
            Map<String, Object> data = new HashMap<>();
            data.put("token", jwtToken);

            return Result.success(data);
        } catch (OAuth2Exception e) {
            return Result.error(e.getMessage());
        }
    }



    public String getAccessToken(String code) {
        if (!StringUtils.hasText(code)) {
            throw new OAuth2Exception("Authorization code cannot be empty");
        }

        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("client_id", githubAppProperties.getClientId());
        params.add("client_secret", githubAppProperties.getClientSecret());
        params.add("code", code);
        params.add("redirect_uri", githubAppProperties.getRedirectUri());

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));


        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(params, headers);

        try {
            ResponseEntity<String> response = restTemplate.exchange(
                    githubAppProperties.getTokenUri(),
                    HttpMethod.POST,
                    requestEntity,
                    String.class
            );

            if (!response.getStatusCode().is2xxSuccessful()) {
                log.error("Failed to get access token. Response: {}", response.getBody());
                throw new OAuth2Exception("Failed to get access token from GitHub");
            }

            return parseAccessToken(response.getBody());
        } catch (Exception e) {
            log.error("Error while getting access token from GitHub", e);
            throw new OAuth2Exception("Failed to get access token from GitHub", e);
        }
    }

    public Map<String, Object> getUserInfo(String accessToken) {
        if (!StringUtils.hasText(accessToken)) {
            throw new OAuth2Exception("Access token cannot be empty");
        }

        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", "Bearer " + accessToken);
        HttpEntity<String> entity = new HttpEntity<>(headers);

        try {
            ResponseEntity<String> response = restTemplate.exchange(
                    githubAppProperties.getUserInfoUri(),
                    HttpMethod.GET,
                    entity,
                    String.class
            );

            if (!response.getStatusCode().is2xxSuccessful()) {
                log.error("Failed to get user info. Response: {}", response.getBody());
                throw new OAuth2Exception("Failed to get user info from GitHub");
            }

            return parseUserInfo(response.getBody());
        } catch (Exception e) {
            log.error("Error while getting user info from GitHub", e);
            throw new OAuth2Exception("Failed to get user info from GitHub", e);
        }
    }

    private String parseAccessToken(String responseBody) {
        try {
            // 先尝试解析为JSON（如果设置了Accept头）
            try {
                JsonNode jsonNode = objectMapper.readTree(responseBody);
                JsonNode tokenNode = jsonNode.get("access_token");
                if (tokenNode != null) {
                    return tokenNode.asText();
                }
            } catch (JsonProcessingException e) {
                // 如果不是JSON，尝试解析为URL编码格式
                log.debug("Response is not JSON, trying URL encoded format");
            }

            // 解析URL编码格式
            String[] pairs = responseBody.split("&");
            for (String pair : pairs) {
                String[] keyValue = pair.split("=");
                if (keyValue.length == 2 && "access_token".equals(keyValue[0])) {
                    return keyValue[1];
                }
            }

            throw new OAuth2Exception("Access token not found in response");
        } catch (Exception e) {
            log.error("Failed to parse access token from response: {}", responseBody, e);
            throw new OAuth2Exception("Failed to parse access token", e);
        }
    }

    private Map<String, Object> parseUserInfo(String responseBody) {
        try {
            return objectMapper.readValue(responseBody, Map.class);
        } catch (Exception e) {
            log.error("Failed to parse user info from response: {}", responseBody, e);
            throw new OAuth2Exception("Failed to parse user info", e);
        }
    }
}
